home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / NNTPCLI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-07  |  20.7 KB  |  766 lines

  1. /*
  2.  *    Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *    Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *
  8.  *    Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *    Permission granted for non-commercial copying and use, provided
  10.  *    this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  */
  32. #include "global.h"
  33. #ifdef    NNTP
  34. #include "files.h"
  35. #include "ctype.h"
  36. #include <time.h>
  37. #ifdef MSDOS
  38. #include <sys/timeb.h>
  39. #endif
  40. #include "timer.h"
  41. #include "socket.h"
  42. #include "usock.h"
  43. #include "commands.h"
  44. #include "smtp.h"
  45. #include "mailutil.h"
  46. #include "hardware.h"
  47.  
  48. #if !defined(_lint)
  49. static char rcsid[] OPTIONAL = "$Id: nntpcli.c,v 1.21 1997/09/07 21:18:28 root Exp root $";
  50. #endif
  51.  
  52. #define NNTPMAXLEN    512
  53.  
  54. struct nntpservers {
  55.     struct timer nntpcli_t;
  56.     char *name;
  57.     char *groups;
  58.     int lowtime, hightime;    /* for connect window */
  59.     struct nntpservers *next;
  60. };
  61.  
  62. #define    NULLNNTP    (struct nntpservers *)NULL
  63.  
  64. #define MAXGROUPDIRS 10
  65.  
  66. extern int Smtpquiet;
  67. extern int mkdir (const char *pathname, mode_t mode);
  68.  
  69. struct grouploc {
  70.     char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  71.     char *directory;    /* directory where these groups should be */
  72. };
  73.  
  74. static struct grouploc groupdirs[MAXGROUPDIRS] =
  75. {
  76.     {NULL, NULL}        /*lint !e785 */
  77. };
  78.  
  79. static struct nntpservers *Nntpservers = NULLNNTP;
  80. static char *Nntpgroups = NULLCHAR;
  81. static unsigned short nntptrace = 1;
  82. static int nntpquiet = 0;
  83. static char *News_spool = NULL;
  84. static int np_all = 0;        /* non-zero if NNTPdir is a malloc'ed space */
  85. static char const *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  86. static char nntpstring[] = "nntp";
  87. static char historystring[] = "history";
  88.  
  89.  
  90. static void nntptick (void *tp);
  91. static void nntp_job (int i1, void *tp, void *v1);
  92. static int gettxt (int s, FILE * fp);
  93. static int getreply (int s);
  94. static int getarticle (int s, char *msgid);
  95. static int dogroups (int argc, char *argv[], void *p);
  96. static int doadds (int argc, char *argv[], void *p);
  97. static int dodrops (int argc, char *argv[], void *p);
  98. static int dokicks (int argc, char *argv[], void *p);
  99. static int dolists (int argc, char *argv[], void *p);
  100. static int donntrace (int argc, char *argv[], void *p);
  101. static int donnquiet (int argc, char *argv[], void *p);
  102. static int dondir (int argc, char *argv[], void *p);
  103.  
  104. /* Tracing levels:
  105.     0 - no tracing
  106.     1 - serious errors reported
  107.     2 - transient errors reported
  108.     3 - session progress reported
  109.     4 - actual received articles displayed
  110.  */
  111.  
  112. static struct cmds Nntpcmds[] =
  113. {
  114.     { "addserver",        doadds,        0, 3, "nntp addserver <nntpserver> <interval>"},
  115.     { "directory",        dondir,        0, 0, NULLCHAR},
  116.     { "dropserver",        dodrops,    0, 2, "nntp dropserver <nntpserver>"},
  117.     { "groups",        dogroups,    0, 0, NULLCHAR},
  118.     { "kick",        dokicks,    0, 2, "nntp kick <nntpserver>"},
  119.     { "listservers",    dolists,    0, 0, NULLCHAR},
  120.     { "quiet",        donnquiet,    0, 0, NULLCHAR},
  121.     { "trace",        donntrace,    0, 0, NULLCHAR},
  122.     { NULLCHAR,        0,        0, 0, NULLCHAR}
  123. };
  124.  
  125.  
  126.  
  127.  
  128. int
  129. donntp (int argc, char *argv[], void *p)
  130. {
  131.     return subcmd (Nntpcmds, argc, argv, p);
  132. }
  133.  
  134.  
  135.  
  136. static int
  137. doadds (int argc, char *argv[], void *p OPTIONAL)
  138. {
  139. struct nntpservers *np;
  140.  
  141.     for (np = Nntpservers; np != NULLNNTP; np = np->next)
  142.         if (stricmp (np->name, argv[1]) == 0)
  143.             break;
  144.  
  145.     if (np == NULLNNTP) {
  146.         np = (struct nntpservers *) callocw (1, sizeof (struct nntpservers));
  147.  
  148.         np->name = strdup (argv[1]);
  149.         np->next = Nntpservers;
  150.         Nntpservers = np;
  151.         np->groups = NULLCHAR;
  152.         np->lowtime = np->hightime = -1;
  153.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  154.         np->nntpcli_t.arg = (void *) np;
  155.     }
  156.  
  157.     if (argc > 3) {
  158.         int i;
  159.  
  160.         if (np->groups == NULLCHAR) {
  161.             np->groups = mallocw (NNTPMAXLEN);
  162.             *np->groups = '\0';
  163.         }
  164.         for (i = 3; i < argc; ++i) {
  165.             if (isdigit (*argv[i])) {
  166.                 int lh, ll, hh, hl;
  167.  
  168.                 sscanf (argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  169.                 np->lowtime = lh * 100 + ll;
  170.                 np->hightime = hh * 100 + hl;
  171.             } else if ((strlen (np->groups) + strlen (argv[i]) + 2) >= NNTPMAXLEN)
  172.                 tprintf ("Group list too long!  Group '%s' ignored!\n", argv[i]);
  173.             else {    /* it's a group, and it fits... add it to list */
  174.                 if (*np->groups != '\0')
  175.                     strcat (np->groups, ",");
  176.                 strcat (np->groups, argv[i]);
  177.             }
  178.         }
  179.         if (*np->groups == '\0') {    /* No groups specified? */
  180.             free (np->groups);
  181.             np->groups = NULLCHAR;
  182.         }
  183.     }
  184.     /* set timer duration */
  185.     set_timer (&np->nntpcli_t, atol (argv[2]) * 1000L);
  186.     start_detached_timer (&np->nntpcli_t);    /* and fire it up */
  187.     return 0;
  188. }
  189.  
  190.  
  191.  
  192. static int
  193. dodrops (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
  194. {
  195. struct nntpservers *np, *npprev = NULLNNTP;
  196.  
  197.     for (np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  198.         if (stricmp (np->name, argv[1]) == 0) {
  199.             stop_timer (&np->nntpcli_t);
  200.             free (np->name);
  201.             if (np->groups)
  202.                 free (np->groups);
  203.             if (npprev != NULLNNTP)
  204.                 npprev->next = np->next;
  205.             else
  206.                 Nntpservers = np->next;
  207.             free ((char *) np);
  208.             return 0;
  209.         }
  210.     tputs ("No such server enabled.\n");
  211.     return 0;
  212. }
  213.  
  214.  
  215.  
  216. static int
  217. dolists (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  218. {
  219. struct nntpservers *np;
  220.  
  221.     for (np = Nntpservers; np != NULLNNTP; np = np->next) {
  222.         char tbuf[80];
  223.  
  224.         if (np->lowtime != -1 && np->hightime != -1)
  225.             sprintf (tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime / 100, np->lowtime % 100, np->hightime / 100, np->hightime % 100);
  226.         else
  227.             tbuf[0] = '\0';
  228.         tprintf ("%-32s (%lu/%lu%s) %s\n", np->name,
  229.              read_timer (&np->nntpcli_t) / 1000L,
  230.              dur_timer (&np->nntpcli_t) / 1000L,
  231.              tbuf, np->groups ? np->groups : "");
  232.     }
  233.     return 0;
  234. }
  235.  
  236.  
  237.  
  238. static int 
  239. donntrace (int argc, char *argv[], void *p OPTIONAL)
  240. {
  241.     return setshort (&nntptrace, "NNTP tracing", argc, argv);
  242. }
  243.  
  244.  
  245.  
  246. static int 
  247. donnquiet (int argc, char *argv[], void *p OPTIONAL)
  248. {
  249.     return setbool (&nntpquiet, "NNTP quiet", argc, argv);
  250. }
  251.  
  252.  
  253.  
  254. static int 
  255. dondir (int argc, char *argv[], void *vp OPTIONAL)
  256. {
  257.     if (argc < 2) {
  258.         int i;
  259.  
  260.         tprintf ("spool: %s\n", News_spool ? News_spool : Mailspool);
  261.         tprintf ("control: %s\n", NNTPdir);
  262.         for (i = 0; i < MAXGROUPDIRS; ++i)
  263.             if (groupdirs[i].prefix)
  264.                 tprintf ("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  265.     } else {
  266.         char *p;
  267.  
  268.         if ((p = strchr (argv[1], '=')) != NULLCHAR) {    /* set a groupdir */
  269.             int i;
  270.  
  271.             *p++ = '\0';
  272.             for (i = 0; i < MAXGROUPDIRS; ++i)
  273.                 if (groupdirs[i].prefix)
  274.                     if (!strnicmp (groupdirs[i].prefix, argv[1], strlen (argv[1]))) {
  275.                         if (groupdirs[i].directory) {
  276.                             free (groupdirs[i].directory);
  277.                             groupdirs[i].directory = NULLCHAR;
  278.                         }
  279.                         if (*p == '\0') {
  280.                             free (groupdirs[i].prefix);
  281.                             groupdirs[i].prefix = NULLCHAR;
  282.                         } else
  283.                             groupdirs[i].directory = strdup (p);
  284.                         return 0;
  285.                     }
  286.             if (*p == '\0')    /* trashing a group that's not there */
  287.                 return 0;
  288.             for (i = 0; i < MAXGROUPDIRS; ++i) {
  289.                 if (groupdirs[i].prefix == NULLCHAR) {
  290.                     groupdirs[i].prefix = strdup (argv[1]);
  291.                     if (groupdirs[i].directory) {
  292.                         free (groupdirs[i].directory);
  293.                         groupdirs[i].directory = NULL;
  294.                     }
  295.                     groupdirs[i].directory = strdup (p);
  296.                     return 0;
  297.                 }
  298.             }
  299.             tputs ("Directory table full\n");
  300.         } else {    /* no '=', so just set default */
  301.             if (News_spool)
  302.                 free (News_spool);
  303.             News_spool = strdup (argv[1]);
  304.         }
  305.         if (argc > 2) {    /* they specified a newsdir as well */
  306.             if (np_all)
  307.                 free (NNTPdir);
  308.             NNTPdir = strdup (argv[2]);
  309.             np_all = 1;
  310.         }
  311.     }
  312.     return 0;
  313. }
  314.  
  315.  
  316.  
  317. static int
  318. dokicks (int argc OPTIONAL, char *argv[], void *p OPTIONAL)
  319. {
  320. struct nntpservers *np;
  321.  
  322.     for (np = Nntpservers; np != NULLNNTP; np = np->next)
  323.         if (stricmp (np->name, argv[1]) == 0) {
  324.             /* If the timer is not running, the timeout function has
  325.             * already been called and we don't want to call it again.
  326.             */
  327.             if (run_timer (&np->nntpcli_t))
  328.                 stop_timer (&np->nntpcli_t);
  329.  
  330.             nntptick ((void *) np);
  331.             return 0;
  332.         }
  333.     tputs ("No such server enabled.\n");
  334.     return 1;
  335. }
  336.  
  337.  
  338.  
  339. static int
  340. dogroups (int argc, char *argv[], void *p OPTIONAL)
  341. {
  342. int i;
  343.  
  344.     if (argc < 2) {
  345.         if (Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp (Nntpgroups, "*") == 0))
  346.             tputs ("All groups are currently enabled.\n");
  347.         else
  348.             tprintf ("Currently enabled newsgroups:\n%s\n", Nntpgroups);
  349.         return 0;
  350.     }
  351.     if (Nntpgroups == NULLCHAR)
  352.         Nntpgroups = mallocw (NNTPMAXLEN);
  353.     *Nntpgroups = '\0';
  354.     for (i = 1; i < argc; ++i) {
  355.         if (i > 1)
  356.             strcat (Nntpgroups, ",");
  357.         strcat (Nntpgroups, argv[i]);
  358.     }
  359.     return 0;
  360. }
  361.  
  362.  
  363.  
  364. /* This is the routine that gets called every so often to connect to
  365.  * NNTP servers.
  366.  */
  367. static void
  368. nntptick (void *tp)
  369. {
  370.     (void) newproc ("NNTP client", 3072, nntp_job, 0, tp, NULL, 0);
  371. }
  372.  
  373.  
  374.  
  375. static void
  376. nntp_job (int i1 OPTIONAL, void *tp, void *v1 OPTIONAL)
  377. {
  378. FILE *fp, *tmpf;
  379. int s = -1, i;
  380. struct tm *ltm;
  381. time_t t;
  382. int now;
  383. struct nntpservers *np = (struct nntpservers *) tp;
  384. struct sockaddr_in fsocket;
  385. char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
  386.  
  387.     if (nntptrace >= 3)
  388.         tcmdprintf ("NNTP daemon entered, target = %s\n", np->name);
  389.  
  390.     (void) time (&t);    /* more portable than gettime() */
  391.     ltm = localtime (&t);
  392.     now = ltm->tm_hour * 100 + ltm->tm_min;
  393.     if (np->lowtime < np->hightime) {    /* doesn't cross midnight */
  394.         if (now < np->lowtime || now >= np->hightime) {
  395.             if (nntptrace >= 3)
  396.                 tcmdprintf ("NNTP window to '%s' not open\n", np->name);
  397.             start_detached_timer (&np->nntpcli_t);
  398.             return;
  399.         }
  400.     } else {
  401.         if (now < np->lowtime && now >= np->hightime) {
  402.             if (nntptrace >= 3)
  403.                 tcmdprintf ("NNTP window to '%s' not open\n", np->name);
  404.             start_detached_timer (&np->nntpcli_t);
  405.             return;
  406.         }
  407.     }
  408.  
  409.     fsocket.sin_addr.s_addr = resolve (np->name);
  410.     if (fsocket.sin_addr.s_addr == 0) {    /* No IP address found */
  411.         if (nntptrace >= 2)
  412.             tcmdprintf ("NNTP can't resolve host '%s'\n", np->name);
  413.         /* Try again later */
  414.         start_detached_timer (&np->nntpcli_t);
  415.         return;
  416.     }
  417.     fsocket.sin_family = AF_INET;
  418.     fsocket.sin_port = IPPORT_NNTP;
  419.  
  420.     s = socket (AF_INET, SOCK_STREAM, 0);
  421.     (void) sockmode (s, SOCK_ASCII);
  422.     if (connect (s, (char *) &fsocket, SOCKSIZE) == -1) {
  423.         const char *cp2;
  424.  
  425.         cp2 = sockerr (s);
  426.         log (s, "NNTP %s Connect failed: %s", psocket (&fsocket),
  427.              cp2 != NULLCHAR ? cp2 : "");
  428.         if (nntptrace >= 2)
  429.             tcmdprintf ("NNTP %s Connect failed: %s\n", psocket (&fsocket),
  430.                     cp2 != NULLCHAR ? cp2 : "");
  431.         goto quit;
  432.     }
  433.     /* Eat the banner */
  434.     i = getreply (s);
  435.     if (i == -1 || i >= 400) {
  436.         log (s, "NNTP %s bad reply on banner (response was %d)", psocket (&fsocket), i);
  437.         if (nntptrace >= 1)
  438.             tcmdprintf ("NNTP %s bad reply on banner (response was %d)\n", psocket (&fsocket), i);
  439.         goto quit;
  440.     }
  441.     if (mlock (NNTPdir, nntpstring)) {
  442.         if (nntptrace >= 2)
  443.             tcmdprintf ("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket (&fsocket));
  444.         goto quit;
  445.     }
  446.     sprintf (buf, "%s/nntp.dat", NNTPdir);
  447.     if ((fp = fopen (buf, UPDATE_TEXT)) == NULLFILE) {
  448.         log (s, "NNTP %s Connect failed: Cannot open %s", psocket (&fsocket),
  449.              buf);
  450.         if (nntptrace >= 1)
  451.             tcmdprintf ("NNTP %s Connect failed: Cannot open %s\n", psocket (&fsocket), buf);
  452.         rmlock (NNTPdir, nntpstring);
  453.         goto quit;
  454.     }
  455.     rewind (fp);
  456.     /*    for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  457.     for (; fgets (buf, NNTPMAXLEN, fp) != NULLCHAR;) {
  458.         if ((cp = strchr (buf, ' ')) == NULLCHAR)
  459.             continue;    /* something wrong with this line, skip it */
  460.         *cp = '\0';
  461.         if (stricmp (buf, np->name) == 0) {
  462.             rip (cp + 1);
  463.             lastdate = strdup (cp + 1);
  464.             break;
  465.         }
  466.     }
  467.     fclose (fp);
  468.     rmlock (NNTPdir, nntpstring);
  469.  
  470.     if (lastdate == NULLCHAR)
  471.         lastdate = strdup ("700101 000000");
  472.     /* snapshot the time for use later in re-writing nntp.dat */
  473.     (void) time (&t);
  474.     ltm = localtime (&t);
  475.  
  476.     /* Get a list of new message-id's */
  477.     if (np->groups) {
  478.         if (nntptrace >= 3)
  479.             tcmdprintf ("==>NEWNEWS %s %s\n", np->groups, lastdate);
  480.         usprintf (s, "NEWNEWS %s %s\n", np->groups, lastdate);
  481.     } else {
  482.         if (nntptrace >= 3)
  483.             tcmdprintf ("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  484.         usprintf (s, "NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  485.     }
  486.     free (lastdate);
  487.     /* Get the response */
  488.     if ((i = getreply (s)) != 230) {    /* protocol error */
  489.         log (s, "NNTP %s protocol error (response was %d)", psocket (&fsocket), i);
  490.         if (nntptrace >= 1)
  491.             tcmdprintf ("NNTP %s protocol error (response was %d)\n", psocket (&fsocket), i);
  492.         goto quit;
  493.     }
  494.     if ((tmpf = tmpfile ()) == NULLFILE) {
  495.         if (nntptrace >= 1)
  496.             tcmdprintf ("NNTP %s Cannot open temp file\n", psocket (&fsocket));
  497.         goto quit;
  498.     }
  499.     if (gettxt (s, tmpf) == -1) {
  500.         log (s, "NNTP %s giving up: gettxt() failure", psocket (&fsocket));
  501.         if (nntptrace >= 1)
  502.             tcmdprintf ("NNTP %s giving up: gettxt() failure\n", psocket (&fsocket));
  503.         fclose (tmpf);
  504.         goto quit;
  505.     }
  506.     /* Open the history file */
  507.     if (mlock (NNTPdir, historystring)) {
  508.         if (nntptrace >= 1)
  509.             tcmdprintf ("NNTP %s giving up: couldn't lock history file\n", psocket (&fsocket));
  510.         fclose (tmpf);
  511.         goto quit;
  512.     }
  513.     sprintf (buf, "%s/history", NNTPdir);
  514.     if ((fp = fopen (buf, APPEND_TEXT)) == NULLFILE) {
  515.         log (s, "NNTP %s Connect failed: Cannot open %s", psocket (&fsocket), buf);
  516.         if (nntptrace >= 1)
  517.             tcmdprintf ("NNTP %s Connect failed: Cannot open %s\n", psocket (&fsocket), buf);
  518.         fclose (tmpf);
  519.         goto quit;
  520.     }
  521.     /* search through the history file for matching message id's */
  522.     rewind (tmpf);
  523.     while (fgets (tbuf, NNTPMAXLEN, tmpf) != NULLCHAR) {
  524.         i = 0;
  525.         rewind (fp);
  526.         while (fgets (buf, NNTPMAXLEN, fp) != NULLCHAR) {
  527.             if (stricmp (buf, tbuf) == 0) {
  528.                 i = 1;
  529.                 break;
  530.             }
  531.             kwait (NULL);
  532.         }
  533.         if (i == 0) {    /* not found, get the article */
  534.             if (getarticle (s, tbuf) == -1) {
  535.                 log (s, "NNTP %s Giving up: could not get article", psocket (&fsocket));
  536.                 if (nntptrace >= 2)
  537.                     tcmdprintf ("NNTP %s Giving up: could not get article\n", psocket (&fsocket));
  538.                 fclose (fp);
  539.                 rmlock (NNTPdir, historystring);
  540.                 fclose (tmpf);
  541.                 goto quit;
  542.             }
  543.             fprintf (fp, "%s", tbuf);    /* add the new message id */
  544.         }
  545.     }
  546.     fclose (fp);
  547.     rmlock (NNTPdir, historystring);
  548.     fclose (tmpf);
  549.     if (nntptrace >= 3)
  550.         tcmdprintf ("==>QUIT\n");
  551.     usprintf (s, "QUIT\n");
  552.     /* Eat the response */
  553.     (void) getreply (s);
  554.     /* NOW, update the nntp.dat file */
  555.     if (mlock (NNTPdir, nntpstring)) {
  556.         if (nntptrace >= 2)
  557.             tcmdprintf ("NNTP %s Could not lock nntp.dat for update\n", psocket (&fsocket));
  558.         goto quit;
  559.     }
  560.     sprintf (buf, "%s/nntp.dat", NNTPdir);
  561.     fp = fopen (buf, READ_TEXT);
  562.     sprintf (buf, "%s/nntp.tmp", NNTPdir);
  563.     if ((tmpf = fopen (buf, WRITE_TEXT)) == NULLFILE)
  564.         if (nntptrace >= 1)
  565.             tcmdprintf ("NNTP %s Cannot create temp file '%s'\n", psocket (&fsocket), buf);
  566.     if (fp == NULLFILE || tmpf == NULLFILE) {
  567.         log (s, "NNTP %s Could not update %s", psocket (&fsocket), buf);
  568.         if (nntptrace >= 2)
  569.             tcmdprintf ("NNTP %s Could not update %s\n", psocket (&fsocket), buf);
  570.         if (fp)
  571.             fclose (fp);
  572.         if (tmpf)
  573.             fclose (tmpf);
  574.         rmlock (NNTPdir, nntpstring);
  575.         goto quit;
  576.     }
  577.     while (fgets (tbuf, sizeof (tbuf), fp))
  578.         if (strnicmp (tbuf, np->name, strlen (np->name)))
  579.             fputs (tbuf, tmpf);
  580.     fprintf (tmpf, "%s %02d%02d%02d %02d%02d%02d\n", np->name, ltm->tm_year % 100, ltm->tm_mon + 1,
  581.          ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
  582.     fclose (fp);
  583.     fclose (tmpf);
  584.     sprintf (buf, "%s/nntp.dat", NNTPdir);
  585.     sprintf (tbuf, "%s/nntp.tmp", NNTPdir);
  586.     (void) unlink (buf);
  587.     (void) rename (tbuf, buf);
  588.     rmlock (NNTPdir, nntpstring);
  589. quit:
  590.     if (nntptrace >= 3)
  591.         tcmdprintf ("NNTP daemon exiting\n");
  592.     close_s (s);
  593.     /* Restart timer */
  594.     start_detached_timer (&np->nntpcli_t);
  595.     return;
  596. }
  597.  
  598.  
  599.  
  600. static int
  601. gettxt (int s, FILE *fp)
  602. {
  603. char buf[NNTPMAXLEN];
  604. int nlines;
  605.  
  606.     for (nlines = 0; recvline (s, (unsigned char *) buf, NNTPMAXLEN) != -1; ++nlines) {
  607.         if (nntptrace >= 4)
  608.             tcmdprintf ("<==%s", buf);
  609.         if (strcmp (buf, ".\n") == 0) {
  610.             if (nntptrace >= 3)
  611.                 tcmdprintf ("NNTP received %d lines\n", nlines);
  612.             return 0;
  613.         }
  614.         /* check for escaped '.' characters */
  615.         if (strcmp (buf, "..\n") == 0)
  616.             fputs (".\n", fp);
  617.         else
  618.             fputs (buf, fp);
  619.     }
  620.     if (nntptrace >= 1)
  621.         tcmdprintf ("NNTP receive error after %d lines\n", nlines);
  622.     return -1;
  623. }
  624.  
  625.  
  626.  
  627. static int
  628. getreply (int s)
  629. {
  630. char buf[NNTPMAXLEN];
  631. int response;
  632.  
  633.     while (recvline (s, (unsigned char *) buf, NNTPMAXLEN) != -1) {
  634.         /* skip informative messages and blank lines */
  635.         if (buf[0] == '\0' || buf[0] == '1')
  636.             continue;
  637.         sscanf (buf, "%d", &response);
  638.         if (nntptrace >= 3)
  639.             tcmdprintf ("<==%s\n", buf);
  640.         return response;
  641.     }
  642.     if (nntptrace >= 3)
  643.         tcmdprintf ("==No response\n");
  644.     return -1;
  645. }
  646.  
  647.  
  648.  
  649. static int
  650. getarticle (int s, char *msgid)
  651. {
  652. char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  653. FILE *fp, *tmpf;
  654. int r;
  655. char *cp;
  656. time_t t;
  657.  
  658.  
  659.     if (nntptrace >= 3)
  660.         tcmdprintf ("==>ARTICLE %s", msgid);
  661.     usprintf (s, "ARTICLE %s", msgid);
  662.     r = getreply (s);
  663.     if (r == -1 || r >= 500)
  664.         return -1;
  665.     if (r >= 400)
  666.         return 0;
  667.     if ((tmpf = tmpfile ()) == NULLFILE) {
  668.         if (nntptrace >= 1)
  669.             tcmdprintf ("NNTP Cannot open temp file for article\n");
  670.         return -1;
  671.     }
  672.     if (gettxt (s, tmpf) == -1) {
  673.         fclose (tmpf);
  674.         return -1;
  675.     }
  676.     /* convert the article into mail format */
  677.     rewind (tmpf);
  678.     froml[0] = '\0';
  679.     newgl[0] = '\0';
  680.     while (fgets (buf, NNTPMAXLEN, tmpf) != NULLCHAR) {
  681.         if (strncmp (buf, Hdrs[FROM], 6) == 0) {
  682.             (void) time (&t);
  683.             rip (&buf[6]);
  684.             sprintf (froml, "From %s %ld\n", &buf[6], t);
  685.             if (newgl[0] != '\0')
  686.                 break;
  687.         }
  688.         if (strncmp (buf, Hdrs[NEWSGROUPS], 12) == 0) {
  689.             strncpy (newgl, &buf[12], NNTPMAXLEN);
  690.             if (froml[0] != '\0')
  691.                 break;
  692.         }
  693.         /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  694.         if (strcmp (buf, "\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  695.             /*            fclose(fp); */
  696.             fclose (tmpf);
  697.             return 0;
  698.         }
  699.     }
  700.     sprintf (buf, "%s/", News_spool ? News_spool : Mailspool);
  701.     for (cp = newgl;; ++cp) {
  702.         if (*cp == '.') {
  703.             mkdir (buf, 0755);    /* create a subdirectory, if necessary */
  704.             strcat (buf, "/");
  705.             continue;
  706.         }
  707.         if (*cp == ',' || *cp == '\n') {
  708.             char tempdir[80], prefix[20], *p;
  709.  
  710.             strncpy (tempdir, buf, 80);
  711.             if ((p = strrchr (tempdir, '/')) != NULLCHAR) {
  712.                 *p++ = '\0';
  713.                 strncpy (prefix, p, 20);
  714.             }
  715.             if (mlock (tempdir, prefix)) {
  716.                 if (nntptrace >= 2)
  717.                     tcmdprintf ("NNTP group '%s' is locked\n", buf);
  718.                 return -1;
  719.             }
  720.             strcat (buf, ".txt");
  721.             /* open the mail file */
  722.             if (nntptrace >= 3)
  723.                 tcmdprintf ("Writing article to '%s'\n", buf);
  724.             if ((fp = fopen (buf, APPEND_TEXT)) != NULLFILE) {
  725.                 fputs (froml, fp);
  726. #ifdef USERLOG
  727.                 /* If the userlog code is enabled, we need a
  728.                  * "Received: " line to get the message id
  729.                  * that is used in it - WG7J
  730.                  */
  731.                 time (&t);
  732.                 fprintf (fp, Hdrs[RECEIVED]);
  733.                 fprintf (fp, "by %s with NNTP\n\tid AA%ld ; %s",
  734.                        Hostname, get_msgid (0), ptime (&t));
  735. #endif
  736.                 rewind (tmpf);
  737.                 while (fgets (buf, NNTPMAXLEN, tmpf) != NULLCHAR) {
  738.                     /* for UNIX mail compatiblity */
  739.                     if (strncmp (buf, "From ", 5) == 0)
  740.                         putc ('>', fp);
  741.                     fputs (buf, fp);
  742.                 }
  743.                 putc ('\n', fp);
  744.                 fclose (fp);
  745.             }
  746.             rmlock (tempdir, prefix);
  747.             if (*cp == '\n')
  748.                 break;
  749.             else
  750.                 sprintf (buf, "%s/", News_spool ? News_spool : Mailspool);
  751.             continue;
  752.         }
  753.         buf[strlen (buf) + 1] = '\0';
  754.         buf[strlen (buf)] = strchr (validchars, tolower (*cp)) ? *cp : '_';
  755.     }
  756.     fclose (tmpf);
  757.     strncpy (buf, msgid, NNTPMAXLEN);    /* Get a copy we can munge */
  758.     rip (buf);                /* remove trailing new-line */
  759.     rip (newgl);                /* ditto */
  760.     if (!nntpquiet)
  761.         tprintf ("New news arrived: %s, article %s%c\n", newgl, buf, Smtpquiet ? ' ' : '\007');
  762.     return 0;
  763. }
  764.  
  765. #endif /* NNTP */
  766.